﻿using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Linq.Expressions;

namespace Sgr.EntityFrameworkCore.ValueConversion
{
    // 已有非可空转换器略 …

    /// <summary>
    /// DateTime? ⇄ DateTime?（统一为 UTC）
    /// 未指定 Kind (Unspecified) 直接视为 UTC（不做时区换算，只补 Kind）
    /// null -> null
    /// Local -> ToUniversalTime()
    /// Utc   -> 原样
    /// 读取时数据库值（无 Kind）统一补为 Utc
    /// </summary>
    public sealed class UtcNullableDateTimeDbConverter : ValueConverter<DateTime?, DateTime?>
    {
        public UtcNullableDateTimeDbConverter(ConverterMappingHints? mappingHints = null)
            : base(ToProviderExpression(), FromProviderExpression(), mappingHints)
        {
        }

        private static Expression<Func<DateTime?, DateTime?>> ToProviderExpression()
            => v => !v.HasValue
                ? v
                : (
                    v.Value.Kind == DateTimeKind.Utc
                        ? v.Value
                        : (
                            v.Value.Kind == DateTimeKind.Local
                                ? v.Value.ToUniversalTime()
                                : DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) // Unspecified 直接标记为 UTC
                          )
                  );

        private static Expression<Func<DateTime?, DateTime?>> FromProviderExpression()
            => v => !v.HasValue
                ? v
                : DateTime.SpecifyKind(v.Value, DateTimeKind.Utc);
    }

    /// <summary>
    /// DateTime? ⇄ DateTime?（统一为 UTC）
    /// 未指定 Kind (Unspecified) 先视为 Local，再转 UTC（适用于历史数据原本是本地时间但缺少 Kind）
    /// null -> null
    /// Local -> ToUniversalTime()
    /// Utc   -> 原样
    /// 读取时数据库值（无 Kind）统一补为 Utc
    /// </summary>
    public sealed class UtcNullableDateTimeUnspecifiedAsLocalDbConverter : ValueConverter<DateTime?, DateTime?>
    {
        public UtcNullableDateTimeUnspecifiedAsLocalDbConverter(ConverterMappingHints? mappingHints = null)
            : base(ToProviderExpression(), FromProviderExpression(), mappingHints)
        {
        }

        private static Expression<Func<DateTime?, DateTime?>> ToProviderExpression()
            => v => !v.HasValue
                ? v
                : (
                    v.Value.Kind == DateTimeKind.Utc
                        ? v.Value
                        : (
                            v.Value.Kind == DateTimeKind.Local
                                ? v.Value.ToUniversalTime()
                                : DateTime.SpecifyKind(v.Value, DateTimeKind.Local).ToUniversalTime() // Unspecified 视为 Local 再转 UTC
                          )
                  );

        private static Expression<Func<DateTime?, DateTime?>> FromProviderExpression()
            => v => !v.HasValue
                ? v
                : DateTime.SpecifyKind(v.Value, DateTimeKind.Utc);
    }
}